{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Selectors\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Index\n",
    "\n",
    "- [Introduction](#introduction)\n",
    "- [Brush Selectors](#brushselectors)\n",
    "- [FastIntervalSelector](#fastintervalselector)\n",
    "- [LassoSelector](#lassoselector)\n",
    "- [IndexSelector](#indexselector)\n",
    "- [MultiSelector](#multiselector)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "symbol = 'Security 1'\n",
    "symbol2 = 'Security 2'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "price_data = pd.DataFrame(np.cumsum(np.random.randn(150, 2).dot([[0.5, 0.4], [0.4, 1.0]]), axis=0) + 100,\n",
    "                          columns=[symbol, symbol2],\n",
    "                          index=pd.date_range(start='01-01-2007', periods=150))\n",
    "\n",
    "dates_actual = price_data.index.values\n",
    "prices = price_data[symbol].values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bqplot import *\n",
    "from bqplot.interacts import (\n",
    "    FastIntervalSelector, IndexSelector, BrushIntervalSelector,\n",
    "    BrushSelector, MultiSelector, LassoSelector,\n",
    ")\n",
    "\n",
    "from ipywidgets import ToggleButtons, VBox, HTML"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction <a class=\"anchor\" id=\"introduction\"></a>\n",
    "\n",
    "Selectors are part of the Interaction Layer (link).\n",
    "They are used to select subparts of Marks, that correspond to different regions on the Figure canvas. Different types of selectors select different types of regions:\n",
    "- `BrushSelector`, `FastIntervalSelector` and `MultiSelector` select rectangular regions\n",
    "- `IndexSelector` selects the elements closest to an abcissa\n",
    "- `LassoSelector` selects elements in a region drawn by the user\n",
    "\n",
    "## How they work\n",
    "\n",
    "bqplot Selectors need to be tied to two other widgets:\n",
    "- One or several marks. Their `selected` attribute, a list of data indices, will be set by the `Selector` instance.\n",
    "- One (1d selection) or two (2d selection) `Scales`. These are the scales that the `Selector` operates on. The `Selector`'s `selected` attribute will be expressed as values of those scales.\n",
    "\n",
    "The `Selector` must then be passed to the desired `Figure`, as its `interaction` attribute.\n",
    "\n",
    "Hopefully this will be clear in the following examples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define scales for the rest of the notebook\n",
    "scales = {'x': DateScale(), 'y': LinearScale()}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Brush Selectors <a class=\"anchor\" id=\"brushselectors\"></a>\n",
    "--------------------\n",
    "\n",
    "Selects a rectangular region of the `Figure`.\n",
    "\n",
    "#### Usage:\n",
    "- Click and drag to create a new brush\n",
    "- Drag the edge of the brush to change its width\n",
    "- Drag the inside of the brush to translate it\n",
    "- Clicking and dragging outside of the brush deletes it and creates a new one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The Mark we want to select subsamples of\n",
    "scatter = Scatter(x=dates_actual, y=prices, scales=scales, colors=['orange'],\n",
    "                 selected_style={'opacity': '1'}, unselected_style={'opacity': '0.2'})\n",
    "# Create the brush selector, passing it its corresponding scale.\n",
    "# Notice that we do not pass it any marks for now\n",
    "brushintsel = BrushIntervalSelector(scale=scales['x'])\n",
    "\n",
    "x_ax = Axis(label='Index', scale=scales['x'])\n",
    "x_ay = Axis(label=(symbol + ' Price'), scale=scales['y'], orientation='vertical')\n",
    "# Pass the Selector instance to the Figure\n",
    "fig = Figure(marks=[scatter], axes=[x_ax, x_ay],\n",
    "             title='''Brush Interval Selector Example. Click and drag on the Figure to action.''',\n",
    "             interaction=brushintsel)\n",
    "\n",
    "# The following text widgets are used to display the `selected` attributes\n",
    "text_brush = HTML()\n",
    "text_scatter = HTML()\n",
    "\n",
    "# This function updates the text, triggered by a change in the selector\n",
    "def update_brush_text(*args):\n",
    "    text_brush.value = \"The Brush's selected attribute is {}\".format(brushintsel.selected)\n",
    "def update_scatter_text(*args):\n",
    "    text_scatter.value = \"The scatter's selected indices are {}\".format(scatter.selected)\n",
    "brushintsel.observe(update_brush_text, 'selected')\n",
    "scatter.observe(update_scatter_text, 'selected')\n",
    "\n",
    "update_brush_text()\n",
    "update_scatter_text()\n",
    "\n",
    "# Display\n",
    "VBox([fig, text_brush, text_scatter])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Linking the brush to the scatter\n",
    "Passing a mark (or several) to the selector, will link the mark's selected indices to the selector. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "brushintsel.marks = [scatter]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "From now on we will stop printing out the selected indices, but rather use the `selected_style` and `unselected_style` attributes of the Marks to check which elements are selected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_figure(selector, **selector_kwargs):\n",
    "    '''\n",
    "    Returns a Figure with a Scatter and a Selector.\n",
    "    \n",
    "    Arguments\n",
    "    ---------\n",
    "    selector: The type of Selector, one of\n",
    "        {'BrushIntervalSelector', 'BrushSelector', 'FastIntervalSelector', 'IndexSelector', 'LassoSelector'}\n",
    "    selector_kwargs: Arguments to be passed to the Selector\n",
    "    '''\n",
    "    scatter = Scatter(x=dates_actual, y=prices, scales=scales, colors=['orange'],\n",
    "                      selected_style={'opacity': '1'}, unselected_style={'opacity': '0.2'})\n",
    "    sel = selector(marks=[scatter], **selector_kwargs)\n",
    "    \n",
    "    text_brush = HTML()\n",
    "    if selector != LassoSelector:\n",
    "        def update_text(*args):\n",
    "            text_brush.value = '{}.selected = {}'.format(selector.__name__, sel.selected)\n",
    "        sel.observe(update_text, 'selected')\n",
    "        update_text()\n",
    "\n",
    "    x_ax = Axis(label='Index', scale=scales['x'])\n",
    "    x_ay = Axis(label=(symbol + ' Price'), scale=scales['y'], orientation='vertical')\n",
    "    fig = Figure(marks=[scatter], axes=[x_ax, x_ay], title='{} Example'.format(selector.__name__),\n",
    "                 interaction=sel)\n",
    "    return VBox([fig, text_brush])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### BrushIntervalSelector on the y-axis\n",
    "The attribute `orientation` can be set to 'vertical' to select on the y-axis. Be careful to pass the corresponding y-scale."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_figure(BrushIntervalSelector, orientation='vertical', scale=scales['y'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2d BrushSelector\n",
    "The `BrushSelector` is 2d, and must be fed 2 scales, `x_scale` and `y_scale`.\n",
    "\n",
    "Note that `BrushSelector.selected` is now 2x2. It is the coordinates of the lower left-hand and upper right-hand corners of the rectangle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_figure(BrushSelector, x_scale=scales['x'], y_scale=scales['y'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "FastIntervalSelector <a class=\"anchor\" id=\"fastintervalselector\"></a>\n",
    "--------------------\n",
    "\n",
    "The FastIntervalSelector is functionally like a BrushIntervalSelector, but provides a more fluid and rapid interaction.\n",
    "\n",
    "#### Usage:\n",
    "- The first click creates the selector.\n",
    "- Moving the mouse up and down widens and narrows the interval width.\n",
    "- Moving the mouse left and right translates the interval left and right.\n",
    "- Subsequent clicks will freeze/unfreeze the interval width\n",
    "- A double-click will freeze both the width and the translation\n",
    "\n",
    "Experiment and get a feel for it in the example below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "create_figure(FastIntervalSelector, scale=scales['x'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As of the latest version, `FastIntervalSelector` is only supported for 1d interaction along the x-axis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LassoSelector <a class=\"anchor\" id=\"lassoselector\"></a>\n",
    "--------------------\n",
    "\n",
    "This 2-D selector enables the user to select multiple sets of data points\n",
    "by drawing lassos on the figure. \n",
    "#### Usage:\n",
    "- Click and drag to draw a new lasso\n",
    "- Click a lasso to select (de-select) it. Mult\n",
    "- Press the 'Delete' button to delete the selected lassos"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_figure(LassoSelector)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "IndexSelector <a class=\"anchor\" id=\"indexselector\"></a>\n",
    "--------------------\n",
    "\n",
    "This 1-D selector selects a unique value on its scale. The attached Mark's selected element is the closest element to that value.\n",
    "#### Usage:\n",
    "- First click creates and activates the selector \n",
    "- Moving the mouse translates the selector\n",
    "- Subsequent clicks freeze/unfreeze the selector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_figure(IndexSelector, scale=scales['x'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "As of the latest version, `IndexSelector` is only supported for interaction along the x-axis."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MultiSelector <a class=\"anchor\" id=\"multiselector\"></a>\n",
    "--------------------\n",
    "\n",
    "This 1-D selector is equivalent to multiple brush selectors.\n",
    "#### Usage:\n",
    "* The first brush works like a regular brush.\n",
    "* `Ctrl + click` creates a new brush, which works like the regular brush. \n",
    "* The `active` brush has a Green border while all the `inactive` brushes have a Red border.\n",
    "* `Shift + click` deactivates the current `active` brush. Now, click on any `inactive` brush to make it `active`.\n",
    "* `Ctrl + Shift + click` clears and resets all the brushes.\n",
    "\n",
    "Each brush has a name (0, 1, 2, ... by default), and the `selected` attribute is a dict `{brush_name: brush_extent}`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_figure(MultiSelector, scale=scales['x'])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
